﻿''' <summary>
''' The INI document reader control.
''' </summary>
''' <remarks></remarks>
Public Class Reader
    Inherits List(Of Section)

#Region "Document Information"

    Private _DocumentFileInfo As IO.FileInfo = Nothing

    Public ReadOnly Property IsProperINI() As Boolean
        Get
            If _DocumentFileInfo.Extension.EndsWith("ini") Then Return True
            Return False
        End Get
    End Property

    Public ReadOnly Property DocumentName() As String
        Get
            Return _DocumentFileInfo.Name
        End Get
    End Property

    Private ReadOnly Property INILines() As String()
        Get
            Dim Text As String = My.Computer.FileSystem.ReadAllText(_DocumentFileInfo.FullName)
            Text = Text.Replace(vbCr, vbNewLine)
            Text = Text.Replace(vbCrLf, vbNewLine)
            Dim splitLines() As String = Text.Split(vbNewLine)
            Dim Lines As New List(Of String)



            For i As Integer = 0 To splitLines.Length - 1
                Dim CurrentLine As String = splitLines(i)

                Lines.Add(CurrentLine.Trim())
            Next
            Return Lines.ToArray()
        End Get
    End Property

    Private ReadOnly Property FirstLineIndex() As Integer
        Get
            Return 0
        End Get
    End Property

    Private ReadOnly Property LastLineIndex() As Integer
        Get
            Return INILines.Count - 1
        End Get
    End Property

#End Region

    Private _CleanOnSave As Boolean = True

    Public Property CleanOnSave() As Boolean
        Get
            Return _CleanOnSave
        End Get
        Set(ByVal value As Boolean)
            _CleanOnSave = value
        End Set
    End Property

    Sub New(ByVal INIPath As String)
        MyBase.New()
        _DocumentFileInfo = New IO.FileInfo(INIPath)
        ReadINI()
    End Sub

#Region "Private Functions"

    Private Function getArrayRange(ByVal Array As String(), ByVal StartIndex As Integer, ByVal StopIndex As Integer) As String()
        Dim result As New List(Of String)
        Dim Count As Integer = Array.Length

        If StopIndex <= Count - 1 Then
            For i As Integer = StartIndex To StopIndex
                result.Add(Array(i))
            Next
        End If

        Return result.ToArray()
    End Function

    Private Function getIndexsOfSections() As Integer()
        Dim List As New List(Of Integer)

        Dim LinesCount As Integer = INILines.Length
        Dim Lines As String() = INILines

        For CurrentIndex As Integer = 0 To LinesCount - 1
            Dim CurrentLine As String = Lines(CurrentIndex).Trim()

            If CurrentLine.StartsWith("[") AndAlso CurrentLine.EndsWith("]") Then
                List.Add(CurrentIndex)
            End If
        Next
        List.Add(LastLineIndex + 1)

        Return List.ToArray()
    End Function

    Private Function getIndexsRangeOfSections() As List(Of Integer())
        Dim INT As Integer() = getIndexsOfSections()
        Dim Group As New List(Of Integer())

        For i As Integer = 0 To INT.Length - 1
            Dim CurrentIndex As Integer = i
            Dim NextIndex As Integer = i + 1

            Dim Current As Integer = Nothing
            Dim [Next] As Integer = Nothing

            If Not CurrentIndex = INT.Length Then
                Current = INT(i)
            End If

            If Not NextIndex = INT.Length AndAlso Not NextIndex = 0 Then
                [Next] = INT(NextIndex) - 1
            End If

            If Not [Next] = 0 Then
                Group.Add(New Integer() {Current, [Next]})
            End If
        Next

        Return Group

    End Function

    Private Function getStringRanges() As List(Of String())
        Dim INT As List(Of Integer()) = getIndexsRangeOfSections()
        Dim Ranges As New List(Of String())

        For Each IntArray As Integer() In INT
            Dim StartIndex As Integer = IntArray(0)
            Dim StopIndex As Integer = IntArray(1)

            Dim Start As String = INILines(StartIndex).Trim()
            Dim [Stop] As String = INILines(StopIndex).Trim()
            Dim Range As String() = getArrayRange(INILines, IntArray(0), IntArray(1))

            Ranges.Add(Range)
        Next

        Return Ranges

    End Function

#End Region

#Region "Private Methods"

    Private Sub ReadINI()
        Dim INT As List(Of String()) = getStringRanges()

        For Each s As String() In INT
            Dim Sex As New Section(s)

            MyBase.Add(Sex)
        Next

    End Sub

#End Region

#Region "Parent function overrides/overloads/shadows."

    Public Overloads Function toString() As String
        Dim Out As String = String.Empty

        For Each Sect As Section In Me
            Out += Sect.toString() ' & vbNewLine
        Next

        Return Out.Trim()
    End Function

    Default Public Shadows Property Item(ByVal Name As String) As Section
        Get
            For Each s As Section In Me
                If s.Name = Name Then
                    Return s
                End If
            Next

            Throw New Exception("The specified section name does NOT exist with-in the document.")
        End Get
        Set(ByVal value As Section)
            Me.Add(value)
        End Set
    End Property

#End Region

#Region "Methods"

    ''' <summary>
    ''' Save the current open document, and make no changes to the documents memory.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub Save()
        If CleanOnSave Then Clean()

        My.Computer.FileSystem.WriteAllText(_DocumentFileInfo.FullName, Me.toString(), False)
    End Sub

    ''' <summary>
    ''' Save (a copy) to the specified location but do NOT load it to memory.
    ''' </summary>
    ''' <param name="Location"></param>
    ''' <remarks></remarks>
    Public Sub Save(ByVal Location As String)
        If CleanOnSave Then Clean()
        My.Computer.FileSystem.WriteAllText(Location, Me.toString(), False)
    End Sub

    ''' <summary>
    ''' Save (a copy) of the current open document and load it up in memory.
    ''' </summary>
    ''' <param name="Location"></param>
    ''' <remarks></remarks>
    Public Sub SaveAs(ByVal Location As String)
        If CleanOnSave Then Clean()
        My.Computer.FileSystem.WriteAllText(Location, Me.toString(), False)

        _DocumentFileInfo = New IO.FileInfo(Location)
        MyBase.Clear()
        ReadINI()
    End Sub

    Public Sub Clean()
RESTART:

        For Each sec As Section In Me

            If sec.toArray().Length = 0 Then
                Me.Remove(sec)
                GoTo RESTART
            End If

            For Each pair As KeyValuePair In sec.toArray()

                If pair.Value.Length = 0 Then
                    sec.Remove(pair)
                    GoTo RESTART
                End If

            Next

        Next
    End Sub

#End Region

#Region "Functions"

    Public Function getSectionNames() As String()
        Dim p As New List(Of String)

        For Each s As Section In Me
            p.Add(s.Name)
        Next

        Return p.ToArray()
    End Function

#End Region

End Class
